package com.gitee.wsl.net.client.form

import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.utils.io.core.*
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads

/**
 * 一个可以向 Ktor 表单请求中输入的文件信息。
 *
 * Tip: Create [InputFile] via static methods in `InputFiles` on JVM.
 *
 * @author ForteScarlet
 */
 interface InputFile {
    /**
     * Include this file to [FormBuilder].
     */
    @Throws(Exception::class)
     fun includeTo(key: String, headers: Headers = Headers.Empty, formBuilder: FormBuilder)

    /**
     * To a [FormPart].
     */
    @Throws(Exception::class)
     fun toFormPart(key: String, headers: Headers = Headers.Empty): FormPart<*>
}

/**
 * Create an instance of [InputFile] from [ByteArray].
 */
@JvmName("of")
@JvmOverloads
 fun InputFile(bytes: ByteArray, defaultHeaders: Headers = Headers.Empty): InputFile =
    ByteArrayInputFile(bytes, defaultHeaders)

private class ByteArrayInputFile(private val bytes: ByteArray, private val defaultHeaders: Headers) : InputFile {
    override fun includeTo(key: String, headers: Headers, formBuilder: FormBuilder) {
        formBuilder.append(key, bytes, defaultHeaders + headers)
    }

    override fun toFormPart(key: String, headers: Headers): FormPart<*> =
        FormPart(key, bytes, defaultHeaders + headers)
}

/**
 * Create an instance of [InputFile] from [InputProvider].
 */
@JvmName("of")
@JvmOverloads
 fun InputFile(input: InputProvider, defaultHeaders: Headers = Headers.Empty): InputFile =
    InputProviderInputFile(input, defaultHeaders)

private class InputProviderInputFile(private val input: InputProvider, private val defaultHeaders: Headers) :
    InputFile {
    override fun includeTo(key: String, headers: Headers, formBuilder: FormBuilder) {
        formBuilder.append(key, input, defaultHeaders + headers)
    }

    override fun toFormPart(key: String, headers: Headers): FormPart<*> =
        FormPart(key, input, defaultHeaders + headers)
}

/**
 * Create an instance of [InputFile] from [ChannelProvider].
 */
@JvmName("of")
@JvmOverloads
 fun InputFile(channel: ChannelProvider, defaultHeaders: Headers = Headers.Empty): InputFile =
    ChannelProviderInputFile(channel, defaultHeaders)

private class ChannelProviderInputFile(private val channel: ChannelProvider, private val defaultHeaders: Headers) :
    InputFile {
    override fun includeTo(key: String, headers: Headers, formBuilder: FormBuilder) {
        formBuilder.append(key, channel, defaultHeaders + headers)
    }

    override fun toFormPart(key: String, headers: Headers): FormPart<*> =
        FormPart(key, channel, defaultHeaders + headers)
}

/**
 * Create an instance of [InputFile] from [ByteReadPacket].
 */
@JvmName("of")
@JvmOverloads
 fun InputFile(byteReadPacket: ByteReadPacket, defaultHeaders: Headers = Headers.Empty): InputFile =
    ByteReadPacketInputFile(byteReadPacket, defaultHeaders)

private class ByteReadPacketInputFile(private val byteReadPacket: ByteReadPacket, private val defaultHeaders: Headers) :
    InputFile {
    override fun includeTo(key: String, headers: Headers, formBuilder: FormBuilder) {
        formBuilder.append(key, byteReadPacket, defaultHeaders + headers)
    }

    override fun toFormPart(key: String, headers: Headers): FormPart<*> =
        FormPart(key, byteReadPacket, defaultHeaders + headers)
}

internal operator fun Headers.plus(other: Headers): Headers {
    if (isEmpty()) return other
    if (other.isEmpty()) return this

    return headers {
        appendAll(this@plus)
        appendAll(other)
    }
}
